package my.shadowsocks.common.encryption.impl;

import my.shadowsocks.common.encryption.AbstractCrypt;
import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.engines.BlowfishEngine;
import org.bouncycastle.crypto.modes.CFBBlockCipher;

import java.io.ByteArrayOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.util.HashMap;
import java.util.Map;

/**
 * Blow fish cipher implementation
 * @author fengbo
 */
public class BlowFishCrypt extends AbstractCrypt {

    private final static String CIPHER_BLOWFISH_CFB = "bf-cfb";

    public static Map<String, String> getCiphers() {
        Map<String, String> ciphers = new HashMap<>(1);
        ciphers.put(CIPHER_BLOWFISH_CFB, BlowFishCrypt.class.getName());
        return ciphers;
    }

    public BlowFishCrypt(String name, String password) {
        super(name, password);
    }

    @Override
    public int getKeyLength() {
        return 16;
    }

    @Override
    protected StreamBlockCipher getCipher(boolean isEncrypted) throws InvalidAlgorithmParameterException {
        BlowfishEngine engine = new BlowfishEngine();
        if (name.equals(CIPHER_BLOWFISH_CFB)) {
            return new CFBBlockCipher(engine, getIVLength() * 8);
        }
        throw new InvalidAlgorithmParameterException(name);
    }

    @Override
    public int getIVLength() {
        return 8;
    }

    @Override
    protected void encrypt0(byte[] data, ByteArrayOutputStream stream) {
        int noBytesProcessed;
        byte[] buffer = new byte[data.length];

        noBytesProcessed = encCipher.processBytes(data, 0, data.length, buffer, 0);
        stream.write(buffer, 0, noBytesProcessed);
    }

    @Override
    protected void decrypt0(byte[] data, ByteArrayOutputStream stream) {
        int noBytesProcessed;
        byte[] buffer = new byte[data.length];

        noBytesProcessed = decCipher.processBytes(data, 0, data.length, buffer, 0);
        stream.write(buffer, 0, noBytesProcessed);
    }
}
